#ifndef __ANIMATION_RANDOMIZER_H__
#define __ANIMATION_RANDOMIZER_H__

#pragma once


//class CCryName;
#include "CryName.h"
#include "IAnimationStateNode.h"

class CRandomAnim
{
public:
	CRandomAnim()
	{
		canBeStartedTwiceInARow = true;
		looping = false;
		allowInterrupt = true;
		probability = 1;
		transitionTime = 0.2f;
		animName = "";
	}

	~CRandomAnim() {};

	bool LoadFromXml(XmlNodeRef node)
	{
		if (strcmpi(node->getTag(), "RandomAnim") != 0)
			return false;

		node->getAttr("canBeStartedTwiceInARow", canBeStartedTwiceInARow);
		node->getAttr("looping", looping);
		node->getAttr("allowInterrupt", allowInterrupt);
		node->getAttr("probability", probability);
		node->getAttr("transitionTime", transitionTime);
		animName = node->getAttr("animName");

		return true;
	}

	void ToXml(XmlNodeRef root)
	{
		XmlNodeRef newNode = root->createNode("RandomAnim" );

		// write name and id of this set
		newNode->setAttr("canBeStartedTwiceInARow", canBeStartedTwiceInARow);
		newNode->setAttr("looping", looping);
		newNode->setAttr("allowInterrupt", allowInterrupt);
		newNode->setAttr("probability", probability);
		newNode->setAttr("transitionTime", transitionTime);
		newNode->setAttr("animName", animName.c_str());

		root->addChild(newNode);
	}

	void SerializeAsFile( bool reading, AG_FILE *file );


	bool operator!= (const CRandomAnim& b) const
	{
		return !( canBeStartedTwiceInARow == b.canBeStartedTwiceInARow &&
			looping == b.looping &&
			allowInterrupt == b.allowInterrupt &&
			probability == b.probability &&
			transitionTime == b.transitionTime &&
			animName == b.animName );
	}


	bool		canBeStartedTwiceInARow;
	bool		looping;
	bool		allowInterrupt;
	int			probability;
	float		transitionTime;
	CCryName	animName;
};

class CRandomGeneralParams 
{
public:
	CRandomGeneralParams()
	{
		switchAssetsOverTime = false;
		timeAlignBetweenAssets = false;
		switchOnNonLoopFinish = false;
		switchProbabilitySeconds = 1.0f;
		initialDelay = 0.0f;
	}

	~CRandomGeneralParams() {};

	bool LoadFromXml(XmlNodeRef node)
	{
		if (strcmpi(node->getTag(), "GeneralParameters") != 0)
			return false;

		node->getAttr("switchAssetsOverTime", switchAssetsOverTime);
		node->getAttr("timeAlignBetweenAssets", timeAlignBetweenAssets);
		node->getAttr("switchOnNonLoopFinish", switchOnNonLoopFinish);
		node->getAttr("switchProbabilitySeconds", switchProbabilitySeconds);
		node->getAttr("initialDelay", initialDelay);

		return true;
	}

	void ToXml(XmlNodeRef root)
	{
		XmlNodeRef newNode = root->createNode("GeneralParameters");

		// write name and id of this set
		newNode->setAttr("switchAssetsOverTime", switchAssetsOverTime);
		newNode->setAttr("timeAlignBetweenAssets", timeAlignBetweenAssets);
		newNode->setAttr("switchOnNonLoopFinish", switchOnNonLoopFinish);
		newNode->setAttr("switchProbabilitySeconds", switchProbabilitySeconds);
		newNode->setAttr("initialDelay", initialDelay);

		root->addChild(newNode);
	}

	//! Saving to/Loading from binary AG file
	void SerializeAsFile( bool reading, AG_FILE *file );


	bool operator!= (const CRandomGeneralParams& b) const
	{
		return !( switchAssetsOverTime == b.switchAssetsOverTime &&
			timeAlignBetweenAssets == b.timeAlignBetweenAssets &&
			switchOnNonLoopFinish == b.switchOnNonLoopFinish &&
			switchProbabilitySeconds == b.switchProbabilitySeconds &&
			initialDelay == b.initialDelay );
	}

	//////////////////////////////////////////////////////////////////////////
	// IF you add members here, update LoadFromXml, ToXml and SerializeAsFile 
	// (and you might need to increase the AG file version number in AnimationGraph.h)

	float		switchProbabilitySeconds;
	float		initialDelay;
	bool		switchAssetsOverTime;
	bool		timeAlignBetweenAssets;
	bool		switchOnNonLoopFinish;
};




class CAnimationRandomizer
{
public:
	virtual	~CAnimationRandomizer(){}
	CAnimationRandomizer();
	CAnimationRandomizer( const CAnimationRandomizer& copyFrom );

	//! Returns the animation name for the last used random asset
	CCryName GetCurrentAnimationName()
	{
		return (m_animIndex != INVALID_INDEX) ? m_animations[m_animIndex].animName : ChooseNextAnimation();
	}

	//! Chooses an animation name from the set, according to probabilities
	//! and whether or not an asset can be started twice in a row
	CCryName ChooseNextAnimation();

	//! Called from Init() of the states that uses it and loads all data needed
	//! It takes the data from the xml file generated by the AG Editor and loads it
	bool Init(XmlNodeRef node, IAnimationGraphPtr pGraph);

	//! This reads or saves the data with the binary AG file
	void SerializeAsFile(bool reading, AG_FILE *file);

	//! Returns false if the randomized assets are not supposed to be
	//! switching automatically after some time.
	//! Returns true if the assets are set up to switch and the randomization
	//! indicates it is time for one. (Call GetNextAssetName once this returns true)
	bool		IsItTimeForAnimationSwitch(float animTime = -1.0f);

	//! Returns whether the asset switching is generally enabled.
	//! This will not indicate that it is time to switch assets. (use ::IsItTimeForAnimationSwitch())
	bool		GetAnimationSwitchingEnabled() const { return m_generalParams.switchAssetsOverTime; }

	//! Modifies the passed in parameters according to the next assets needs (looping, time-warping and transition time)
	void		SetAnimationParams( CryCharAnimationParams& params ) const;

	//!  To be called on EnterState
	void		EnterState();

	//! To be called on LeaveState
	void		LeaveState();
	
	//!  To be called on Update
	void		Update();

	//!  Return whether initialDelaySeconds has been set or not
	bool		HasInitialDelay() const;

	virtual void		GetMemoryUsage(ICrySizer * s) const
	{
		s->AddObject(this, sizeof(*this));
	}

private:

	//!  Sums up the probabilities of all animations in the vector and stores
	void CalculateProbabilitySum();

	//! Default value for m_animIndex to indicate no animation has been selected
	static const size_t INVALID_INDEX = ~0;

	CRandomGeneralParams				m_generalParams;

	std::vector<CRandomAnim> 		m_animations;
	
	float		m_probabilitySum;

	// ***FIXME***: The following members are all per-entity state, and yet are shared by the 
	// same AGAnimation & AGAdditive nodes. Until this is fixed expect unpredictable behavior!

	//! The index of the currently selected animation or ~0
	size_t	m_animIndex;
	float		m_elapsedTimeFromEnterState;
	bool		m_initialDelayApplied;
};



#endif // __ANIMATION_RANDOMIZER_H__
